package tlog

import (
	"context"
	"fmt"
	"github.com/didi/tg-flow/tg-core/conf"
	"github.com/didi/tg-flow/tg-core/model"
	"git.xiaojukeji.com/gobiz/logger"
	trace "git.xiaojukeji.com/lego/context-go"
	"log"
	"path/filepath"
	"runtime"
	"strings"
	statsd "go.intra.xiaojukeji.com/foundation/didi-standard-lib/metric/golang-v2/statsdlib"
)

var Handler logger.Tracer

const (
	DIDI_HINT_CODE        = "1"   // 压测
	DIDI_HINT_CODE_TEST   = "2"   // 线上巡检流量
)

func init() {
	log.Println("tg-core tlog init start...")
	var err error
	Handler, err = logger.NewTracerWithConfig(conf.Handler)
	if err != nil {
		log.Fatal("Init log error: ", err)
	}

	Handler.RegisterContextFormat(trace.FormatCtx)
	logger.RegisterContextFormat(trace.FormatCtx)


	err = logger.NewLoggerWithConfig(conf.Handler)
	if err != nil {
		log.Fatal("NewLoggerWithConfig Init log error: ", err)
	}
	initHintLog()
}

// didi 日志跟压测日志分离，注册压测日志句柄&回调函数
func initHintLog()  {
	enable, err := conf.Handler.GetBoolSetting("log", "hint_log.enable")
	if err != nil || !enable {
		return
	}

	prefix, _ := conf.Handler.GetSetting("log","hint_log.prefix")
	dir, _ := conf.Handler.GetSetting("log","hint_log.dir")
	formatInfo, _ := conf.Handler.GetSetting("log","hint_log.format")
	levelInfo, _ := conf.Handler.GetSetting("log","hint_log.level")
	levelVal := getLogLevel(levelInfo)
	async, _ := conf.Handler.GetBoolSetting("log","hint_log.async")
	disableLink, _ := conf.Handler.GetBoolSetting("log","hint_log.disable_link")
	autoClear, _ := conf.Handler.GetBoolSetting("log","hint_log.auto_clear")
	clearHour, _ := conf.Handler.GetIntSetting("log","hint_log.clear_hours")
	seprated, _ := conf.Handler.GetBoolSetting("log","hint_log.seprated")

	err = logger.NewShadowWithSetting( &logger.LogSetting{
		LogT:                 logger.LogTypeFile,
		Enable:               enable,
		Prefix:               prefix,
		AsyncWrite:           async,
		Dir:                  dir,
		DisableLink:          disableLink,
		Format:               formatInfo,
		Level:                levelVal,
		AutoClear:            autoClear,
		ClearHours:           int32(clearHour),
		Seprated:             seprated,
		RMode:                logger.RotateModeHour,
	})

	// 设置压测识别回调函数，这里主要是从ctx 中识别出流量是否含有压测
	logger.RegisterGetShadowFromContext(GetShadowFromContext)
}

// GetShadowFromContext ctx 中放的是lego/trace
func GetShadowFromContext(ctx context.Context) (ret bool) {
	traceInfo := trace.GetTrace(ctx)
	if traceInfo == nil {
		return false
	}
	if traceInfo.GetHintCode() == DIDI_HINT_CODE || traceInfo.GetHintCode() == DIDI_HINT_CODE_TEST{
		return true
	}
	return false
}


func getLogLevel(l string) logger.Level {
	switch strings.ToUpper(l) {
	case "TRACE":
		return logger.TRACE
	case "DEBUG":
		return logger.DEBUG
	case "INFO":
		return logger.INFO
	case "WARNING":
		return logger.WARNING
	case "ERROR":
		return logger.ERROR
	case "FATAL":
		return logger.FATAL
	default:
		return logger.ERROR
	}
}

/**
	ctx：    请求上下文信息
	sc：     业务逻辑中间结果
    tag：    日志的标识，便于查找日志
    etype：  日志类型，主要包含日志所在的函数名，及唯一标识，便于odin监控分类统计
    content：日志内容
    err：    日志对应的错误信息，可为nil
**/
func LogError(ctx context.Context, sc *model.StrategyContext, tag string, etype string, content string, err error) {
	filename, line, path, ok, index := "", 0, "", false, 1
	for {
		if index >= 10 {
			break
		}
		_, filename, line, ok = runtime.Caller(index)
		if ok {
			filename = filepath.Base(filename)
		}
		if filename == "" {
			break
		}
		index++
		if strings.Contains(filename, "autogenerated") || strings.Contains(filename, "asm_amd64") {
			continue
		}
		if len(path) > 0 {
			path = filename + ":" + fmt.Sprintf("%v", line) + "/" + path
		} else {
			path = filename + ":" + fmt.Sprintf("%v", line)
		}
	}
	Handler.Errorf(ctx, tag, "etype=%v||log_path=%v||%v||error=%v", strings.Replace(etype, "\n", "", -1), path, strings.Replace(content, "\n", "", -1), err)
}

/**
	ctx：    请求上下文信息
	sc：     业务逻辑中间结果
    tag：    日志的标识，便于查找日志
    etype：  日志类型，主要包含日志所在的函数名，及唯一标识，便于odin监控分类统计
    content：日志内容
**/
func LogErrorInfo(ctx context.Context, sc *model.StrategyContext, tag string, etype string, content string) {
	filename, line, path, ok, index := "", 0, "", false, 1
	for {
		if index >= 10 {
			break
		}
		_, filename, line, ok = runtime.Caller(index)
		if ok {
			filename = filepath.Base(filename)
		}
		if filename == "" {
			break
		}
		index++
		if strings.Contains(filename, "autogenerated") || strings.Contains(filename, "asm_amd64") {
			continue
		}
		if len(path) > 0 {
			path = filename + ":" + fmt.Sprintf("%v", line) + "/" + path
		} else {
			path = filename + ":" + fmt.Sprintf("%v", line)
		}
	}
	Handler.Errorf(ctx, tag, "etype=%v||log_path=%v||error=%v", strings.Replace(etype, "\n", "", -1), path, strings.Replace(content, "\n", "", -1))
	//上报error统计
	statsd.Counter(etype)
}

/**
	打错误日志，并上报错误
	后续将替代上面2个函数s
 */
func ErrorCount(ctx context.Context, metricName string, content string) {
	path := FormatLogPath()
	Handler.Errorf(ctx, " "+metricName, "etype=%v||log_path=%v||error=%v", metricName, path, strings.Replace(content, "\n", "", -1))
	//上报error统计
	statsd.Counter(metricName)
}

/**
	打印错误日志，根据tags上报错误
 */
func ErrorCountWithTags(ctx context.Context, metricName string, tags map[string]string, content string) {
	path := FormatLogPath()
	Handler.Errorf(ctx, " "+metricName, "etype=%v||log_path=%v||error=%v", metricName, path, strings.Replace(content, "\n", "", -1))
	//上报error统计
	statsd.Counter(metricName, tags)
}


func FormatLogPath() string {
	filename, line, path, ok, index := "", 0, "", false, 1
	for {
		if index >= 10 {
			break
		}
		_, filename, line, ok = runtime.Caller(index)
		if ok {
			filename = filepath.Base(filename)
		}
		if filename == "" {
			break
		}
		index++
		if strings.Contains(filename, "autogenerated") || strings.Contains(filename, "asm_amd64") {
			continue
		}
		if len(path) > 0 {
			path = filename + ":" + fmt.Sprintf("%v", line) + "/" + path
		} else {
			path = filename + ":" + fmt.Sprintf("%v", line)
		}
	}
	return path
}